home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1998 November: Tool Chest / Dev.CD Nov 98 TC.toast / Sample Code / QuickDraw / OffSample / Offscreen.inc1.p < prev    next >
Encoding:
Text File  |  1994-11-18  |  26.9 KB  |  877 lines  |  [TEXT/MPS ]

  1. {------------------------------------------------------------------------------
  2. #
  3. #    Apple Macintosh Developer Technical Support
  4. #
  5. #    "Skippy White's Famous High Level Off-Screen Map Routines”
  6. #
  7. #    Offscreen.inc1.p    -    Pascal Source
  8. #
  9. #    Copyright © 1989 Apple Computer, Inc.
  10. #    All rights reserved.
  11. #    The characters depicted herein (except Skippy White) are fictitious.
  12. #
  13. #    Versions:    
  14. #                1.00                04/89
  15. #                1.01                06/92
  16. #
  17. #    Components:    
  18. #                Offscreen.p            April 1, 1988
  19. #                Offscreen.inc1.p    April 1, 1988
  20. #
  21. #    These routines provide a high-level interface to the QuickDraw & Color
  22. #    Manager routines which allow the creation and manipulation of off-screen
  23. #    bitmaps and pixmaps. They are designed to run on any machine with 128K or
  24. #    later ROMs (sorry 64K ROM fans).
  25. #    Note that the design incorporates the idea that you can go along pretending
  26. #    there is an offscreen buffer even when one couldn’t be allocated, and the
  27. #    calls will do nothing.
  28. #
  29. ------------------------------------------------------------------------------}
  30.  
  31. CONST
  32.     noDepth = -2;
  33.     chunkyPix = 0;
  34.  
  35. TYPE
  36.     PrivateHandle    = ^PrivatePtr;
  37.     PrivatePtr        = ^PrivateRecord;
  38.     PrivateRecord    = RECORD
  39.         requestedBounds    : Rect; {boundary rectangle for the pixmap}
  40.         {used to determine the depth, size, and dimensions of the pixmap}
  41.  
  42.         requestedDepth    : INTEGER; {the requested depth of this map}
  43.         {if > 0, a private device will be created for the map}
  44.  
  45.         requestedColors    : CTabHandle; {color table}
  46.         {our own copy of the color table that was passed in}
  47.  
  48.         requestedPolite    : BOOLEAN; {whether he wanted it polite}
  49.  
  50.         bits            : Handle; {handle to the off-screen bits}
  51.         {if NIL, there is no port or graphics device either}
  52.         notOurs            : BOOLEAN; {TRUE means that we didn’t create the bits}
  53.         needs32Bits        : BOOLEAN; {you must be in 32 bit memory mode to get to bits}
  54.  
  55.         bitsPort        : CGrafPtr; {GrafPort used for the off-screen map.}
  56.         bitsDevice        : GDHandle; {GDevice associated with the map}
  57.         {private device (if requestedDepth > 0)}
  58.         {(from the device list for requestedDepth = kMaxDepth)}
  59.  
  60.         oldSeed            : LONGINT; {old CTSeed for the pixmap's color table}
  61.         {used to detect color updates}
  62.         
  63.         invalRegion        : RgnHandle; {part of offscreen buffer that is invalid}
  64.  
  65.         drawingPort        : GrafPtr; {supplied to BeginOffscreenDahling}
  66.         savedPort        : GrafPtr; {saves old GrafPort while drawing off screen}
  67.         savedMap        : BitMap; {used to save map when we don’t use our port}
  68.         savedVisRgn        : RgnHandle; {saves the visRgn from a user-supplied port}
  69.         savedDevice        : GDHandle; {saves old GDevice while drawing off screen}
  70.     END; {PrivateRecord}
  71.  
  72.  
  73.  
  74. VAR
  75.     theMac    : SysEnvRec;
  76.  
  77.  
  78. {---------- low-level service routines used by the rest of Offscreen ----------}
  79.  
  80. {Fill out the pixmap according to the bounds, pixelType, pixelSize, cmpCount,
  81.  cmpSize and pmTable parameters. All fields will be modified.
  82.  The initial pixmap will be copied from the current GDevice’s pixmap, so you must modify
  83.  the pmap^^.baseAddr before using it.
  84.  dataSize will return with the number of bytes required for the pixel data.
  85.  Note: it is the responsibility of the caller to fill in the baseAddr field
  86.  of the pixmap. You may want to call NewImprovedGBuffer to see if the system will
  87.  allocate the data space and then set baseAddr to point to it.
  88.  This call cannot fail.}
  89. PROCEDURE InitGBufferPixmap(pmap: PixMapHandle; aBounds: Rect; aPixelType, aPixelSize,
  90.      aCmpCount, aCmpSize: INTEGER; aPMTable: CTabHandle; VAR dataSize: LONGINT);
  91.     
  92. VAR
  93.     theGDevice:    GDHandle;
  94. BEGIN
  95.     theGDevice := GetGDevice;                {get the current device}
  96.     pmap^^:=theGDevice^^.gdPMap^^;            {start out with device’s pixmap}
  97.     WITH pmap^^ DO BEGIN
  98.         bounds := aBounds;
  99.         pixelType := aPixelType;
  100.         pixelSize := aPixelSize;
  101.          cmpCount := aCmpCount;
  102.         cmpSize := aCmpSize;
  103.         pmTable := aPMTable;
  104.         WITH bounds DO BEGIN
  105.             rowBytes := ((pixelSize * right + 15) DIV 16) * 2;
  106.             {calculate an even # of words}
  107.             dataSize := bottom * LONGINT(rowBytes); {calculate the size}
  108.             rowBytes := rowBytes + $8000;    {flag that it’s a pixmap}
  109.         END; {WITH}
  110.     END; {WITH}
  111. END; {InitGBufferPixmap}
  112.     
  113. {Fill out a pixmap for use with an onscreen grafPort. It differs from InitGBufferPixmap
  114.  in that it associates the buffer with an onscreen device. This will be the maximim-
  115.  depth device which intersects the globalBounds rectangle. In the most common case
  116.  you will pass in the portRect of a window after converting it to global coordinates.
  117.  If no screen device intersects globalBounds, the function will return FALSE and
  118.  device will return with NIL. If it returns TRUE then device will be a handle to
  119.  the proper GDevice (see StartLeech, below).
  120.  For the other information, see the comments at InitGBufferPixmap.}
  121. FUNCTION InitGBufferScreen(pmap: PixMapHandle; globalBounds: Rect; VAR device:GDHandle;
  122.       VAR dataSize: LONGINT): BOOLEAN;
  123.  
  124. VAR
  125.     savedDevice:    GDHandle;
  126. BEGIN
  127.     InitGBufferScreen := FALSE;
  128.     
  129.     device := GetMaxDevice(globalBounds);        {maximum-depth device for globalBounds}
  130.     IF device <> NIL THEN BEGIN
  131.         savedDevice := GetGDevice;
  132.         
  133.         WITH globalBounds DO
  134.             SetRect(globalBounds, 0, 0, right - left, bottom - top);
  135.         {make globalBounds into a window-ish (zero-based) rectangle}
  136.             
  137.         SetGDevice(device);                        {set to device for InitGBufferPixmap}
  138.         WITH device^^.gdPMap^^ DO
  139.             InitGBufferPixmap(pmap, globalBounds, pixelType, pixelSize,
  140.                  cmpCount, cmpSize, pmTable, dataSize);
  141.             {set up the pixmap}
  142.             
  143.         SetGDevice(savedDevice);
  144.         InitGBufferScreen := TRUE;
  145.     END; {IF device <> NIL}
  146. END; {InitGBufferScreen}
  147.  
  148. {Allocate the data space for the bits of an offscreen buffer. Only do so if
  149.  it can be allocated in an optimum place for graphics use. If the system
  150.  doesn’t have a superior place to allocate the bits, NIL is returned.
  151.  needs32Bits will return TRUE if the bit space requires 32 bit addressing
  152.  in order to access it. buffNotNeeded will return TRUE if performance will be
  153.  improved by NOT buffering (“performance” means avoiding flicker, etc.)}
  154. FUNCTION NewImprovedGBuffer(dataSize: LONGINT; VAR needs32Bits: BOOLEAN;
  155.             VAR buffNotNeeded: BOOLEAN): Ptr;
  156.  
  157. BEGIN
  158.     NewImprovedGBuffer := NIL;
  159.     needs32Bits := FALSE;
  160.     buffNotNeeded := FALSE;
  161. END; {NewImprovedGBuffer}
  162.  
  163. {Free a buffer allocated via NewImprovedGBuffer.}
  164. PROCEDURE FreeEnhancedGBuffer(bits: Ptr);
  165.  
  166. BEGIN
  167. END; {FreeEnhancedGBuffer}
  168.  
  169. {Fill out the GDevice record and set device^^.gdPMap = pixmap. Errors can occur from not
  170.  being able to build the ITable, etc.}
  171. FUNCTION InitGBufferDevice(device: GDHandle; pmap: PixMapHandle): OSErr;
  172.  
  173. VAR
  174.     preferredResolution:    INTEGER;
  175.     inverseTable:             ITabHandle;
  176.  
  177.     PROCEDURE Out(error: OSErr);
  178.     BEGIN {Out}
  179.         IF error <> noErr THEN BEGIN
  180.             DisposeHandle(Handle(device^^.gdITable));    {get rid of the inverse table}
  181.             InitGBufferDevice := error;
  182.             EXIT(InitGBufferDevice);
  183.         END; {IF error <> noErr}
  184.     END; {Out}
  185.  
  186. BEGIN
  187.     device^^.gdITable := NIL;
  188.     inverseTable := ITabHandle(NewHandle(0));    {create the inverse table stub}
  189.     Out(MemError);                                {bail if couldn’t make the handle}
  190.     MakeITable(pmap^^.pmTable, inverseTable, preferredResolution);
  191.     Out(QDError);                                {bail if MakeITable failed}
  192.     
  193.     preferredResolution := GetMainDevice^^.gdResPref; {preferred resolution ??? is this cool?}
  194.     
  195.     WITH device^^ DO BEGIN
  196.         gdType := clutType;                        {no private devices for direct ones, right?}    
  197.         gdITable := inverseTable;
  198.         gdResPref := preferredResolution;
  199.         gdFlags := 2 ** noDriver;
  200.         gdPMap := pmap;
  201.         gdRect := pmap^^.bounds;
  202.     END; {WITH}
  203.     
  204.     InitGBufferDevice := noErr;
  205. END; {InitGBufferDevice}
  206.  
  207.  
  208. {---------- end of low-level service routines ----------}
  209.  
  210.  
  211. PROCEDURE InitOffscreen;
  212. {128K ROMs and new enough Palette Manager}
  213.  
  214. VAR
  215.     error    : OSErr;
  216.  
  217. BEGIN {InitOffscreen}
  218.     error := SysEnvirons(1, theMac);
  219. END; {InitOffscreen}
  220.  
  221.  
  222. (* ??? commented out since we no longer use it (but someone may want it, so should we provide it as a utility?)
  223. FUNCTION GetMaxAreaDevice(globalRect: Rect): GDHandle;
  224. {Find the greatest overlap device for the given global rectangle.}
  225.  
  226. VAR
  227.     area            : LONGINT;
  228.     maxArea            : LONGINT;
  229.     device            : GDHandle;
  230.     intersection    : Rect;
  231.  
  232. BEGIN {GetMaxAreaDevice}
  233.     GetMaxAreaDevice := NIL;
  234.  
  235.     maxArea := 0;
  236.  
  237.     device := GetDeviceList;
  238.     WHILE device <> NIL DO BEGIN
  239.         IF TestDeviceAttribute(device, screenDevice) THEN
  240.             IF TestDeviceAttribute(device, screenActive) THEN
  241.                 IF SectRect(globalRect, device^^.gdRect, intersection) THEN BEGIN
  242.                     WITH intersection DO
  243.                         area := LONGINT(right - left) * LONGINT(bottom - top);
  244.                     IF area > maxArea THEN BEGIN
  245.                         GetMaxAreaDevice := device;
  246.                         maxArea := area;
  247.                     END; {IF area > maxArea}
  248.                 END; {IF SectRect...}
  249.         device := GetNextDevice(device);
  250.     END; {WHILE device <> NIL}
  251. END; {GetMaxAreaDevice}
  252. *)
  253.  
  254.  
  255.  
  256. FUNCTION MakeCleanColorTable(colors: CTabHandle; depth: INTEGER;
  257.                              VAR table: CTabHandle): OSErr;
  258. {This call takes a handle to a list of colors to be included in a color table.
  259.  It returns a table suitable for use for a PixMap, with black and white at the
  260.  correct offsets. If there is no default color table for the depth in question,
  261.  it returns NIL for the table.}
  262.  
  263.     FUNCTION IsWhite(rgb: RGBColor): BOOLEAN;
  264.     BEGIN {IsWhite}
  265.         IsWhite := (rgb.red = $FFFF) & (rgb.green = $FFFF) & (rgb.blue = $FFFF);
  266.     END; {IsWhite}
  267.  
  268.     FUNCTION IsBlack(rgb: RGBColor): BOOLEAN;
  269.     BEGIN {IsBlack}
  270.         IsBlack := (rgb.red = 0) & (rgb.green = 0) & (rgb.blue = 0);
  271.     END; {IsBlack}
  272.  
  273. VAR
  274.     tableIndex    : INTEGER;
  275.     colorIndex    : INTEGER;
  276.     aColor        : RGBColor;
  277.  
  278. BEGIN {MakeCleanColorTable}
  279.     table := GetCTable(depth);
  280.     IF table = NIL THEN
  281.         MakeCleanColorTable := memFullErr
  282.     ELSE
  283.         WITH table^^ DO BEGIN
  284.             ctSeed := GetCTSeed; {get a fresh seed; we'll change the color table}
  285.  
  286.             tableIndex := 1; {skip white in table we are making}
  287.             colorIndex := 0; {start with first color}
  288.  
  289.             REPEAT
  290.                 IF colorIndex > colors^^.ctSize THEN
  291.                     Leave; {done with all the colors}
  292.                 aColor := colors^^.ctTable[colorIndex].rgb;
  293.                 colorIndex := colorIndex + 1; {advance to next color}
  294.                 IF IsWhite(aColor) THEN
  295.                     Cycle; {this one is white, try another}
  296.                 IF IsBlack(aColor) THEN
  297.                     Cycle; {this one is black, try another}
  298.  
  299.         {$PUSH} {$R-}
  300.                 ctTable[tableIndex].rgb := aColor; {use this color}
  301.         {$POP}
  302.                 tableIndex := tableIndex + 1;
  303.             UNTIL tableIndex > ctSize - 1; {until the table is full}
  304.  
  305.             MakeCleanColorTable := noErr;
  306.         END; {WITH}
  307. END; {MakeCleanColorTable}
  308.  
  309.  
  310.  
  311. PROCEDURE DeallocateBits(offscreenHandle: Handle);
  312. {Get rid of the bits from an off-screen map.}
  313.  
  314. BEGIN {DeallocateBits}
  315.     IF offscreenHandle <> NIL THEN
  316.         WITH PrivateHandle(offscreenHandle)^^ DO BEGIN
  317.             IF NOT notOurs THEN
  318.                 DisposeHandle(bits) {get rid of the bits}
  319.             ELSE
  320.                 FreeEnhancedGBuffer(Ptr(bits));
  321.             bits := NIL;
  322.  
  323.             IF bitsPort <> NIL THEN BEGIN
  324.                 IF bitsPort^.visRgn <> NIL THEN BEGIN {visRgn of NIL means port is not open yet}
  325.                     IF requestedDepth > 0 THEN {if we created a private device and color table}
  326.                         DisposeHandle(Handle(bitsPort^.portPixMap^^.pmTable)); {get rid of the colors}
  327.                     ClosePort(GrafPtr(bitsPort));
  328.             
  329.                 DisposeRgn(invalRegion); {get rid of the invalidation region}
  330.                 END; {IF ...bitsPort^.visRgn <> NIL}
  331.                 DisposePtr(Ptr(bitsPort));
  332.             END; {IF bitsPort <> NIL}
  333.             bitsPort := NIL;
  334.  
  335.             IF bitsDevice <> NIL THEN
  336.                 IF requestedDepth > 0 THEN BEGIN {if we created a private device}
  337.                     bitsDevice^^.gdPMap := NIL; {the pixmap was owned by the port}
  338.                     DisposeGDevice(bitsDevice);
  339.                     bitsDevice := NIL;
  340.                 END; {IF requestedDepth > 0}
  341.         END; {WITH}
  342. END; {DeallocateBits}
  343.  
  344.  
  345.  
  346. FUNCTION GetMap(offscreenHandle: Handle): BitMapPtr;
  347. {Get a BitMapPtr for the bits in the offscreen handle.
  348.  This is also the best way to see if the bits are allocated.
  349.  Note that this has a side-effect of setting up the baseAddr properly.}
  350.  
  351. VAR
  352.     map    : BitMapPtr;
  353.  
  354. BEGIN {GetMap}
  355.     map := NIL;
  356.  
  357.     IF offscreenHandle <> NIL THEN {if we have bits}
  358.         WITH PrivateHandle(offscreenHandle)^^ DO BEGIN
  359.             IF bits <> NIL THEN {if we seem to have bits, make sure that we really do}
  360.                 IF StripAddress(bits^) = NIL THEN
  361.                     DeallocateBits(offscreenHandle)
  362.                 ELSE BEGIN
  363.                     {find the map}
  364.                     WITH bitsPort^ DO
  365.                         IF portVersion > 0 THEN {old-style port?}
  366.                             map := @portPixMap {this is really portBits!}
  367.                         ELSE
  368.                             map := BitMapPtr(portPixMap^);
  369.                     IF notOurs THEN
  370.                         map^.baseAddr := Ptr(bits)
  371.                     ELSE
  372.                         map^.baseAddr := bits^;
  373.                 END; {ELSE}
  374.         END; {WITH}
  375.     GetMap := map;
  376. END; {GetMap}
  377.  
  378.  
  379.  
  380. FUNCTION GetBitsHandle(offscreenHandle: Handle): Handle;
  381. BEGIN
  382.     GetBitsHandle := NIL;
  383.     IF offscreenHandle <> NIL THEN
  384.         WITH PrivateHandle(offscreenHandle)^^ DO
  385.             IF NOT notOurs THEN
  386.                 GetBitsHandle := bits;
  387. END; {GetBitsHandle}
  388.  
  389.  
  390.  
  391. FUNCTION AllocateBits(offscreenHandle: Handle; VAR buffNotNeeded: BOOLEAN): OSErr;
  392. {Allocate the bits, port, device, etc. for the off-screen map.
  393.  If an error code is returned, the handle will be in the "dormant" stage. ??? define
  394.  If the bits are already allocated, then nothing will happen.}
  395.  
  396. VAR
  397.     oldPort                : GrafPtr;
  398.     bounder                : Rect;
  399.     map                    : BitMapPtr;
  400.     bitsSize            : LONGINT;
  401.     colors                : CTabHandle;
  402.     preferredResolution    : INTEGER;
  403.     inverseTable        : ITabHandle;
  404.     totalSpace            : LONGINT;
  405.     contiguousSpace        : LONGINT;
  406.     use32Bits            : BOOLEAN;
  407.  
  408.     PROCEDURE Out(error: OSErr);
  409.     BEGIN {Out}
  410.         IF error <> noErr THEN BEGIN
  411.             HUnlock(offscreenHandle);
  412.             DeallocateBits(offscreenHandle);
  413.             SetPort(oldPort);
  414.             AllocateBits := error;
  415.             EXIT(AllocateBits);
  416.         END; {IF error <> noErr}
  417.     END; {Out}
  418.  
  419. BEGIN {AllocateBits}
  420.     IF offscreenHandle <> NIL THEN
  421.         IF GetMap(offscreenHandle) = NIL THEN BEGIN {if the bits are not already allocated}
  422.             GetPort(oldPort);
  423.  
  424.             MoveHHi(offscreenHandle); {fly the handle high in the heap since we lock it down}
  425.             HLock(offscreenHandle);
  426.             WITH PrivateHandle(offscreenHandle)^^ DO BEGIN
  427.                 bitsPort := CGrafPtr(NewPtrClear(SIZEOF(GrafPort))); {create a new port}
  428.                 {NewPtrClear means that bits := NIL, invalRegion := NIL, notOurs := FALSE, etc.}
  429.                 Out(MemError);
  430.  
  431.                 WITH requestedBounds DO
  432.                     SetRect(bounder, 0, 0, right - left, bottom - top);
  433.  
  434.                 IF theMac.hasColorQD THEN
  435.                     WITH bitsPort^ DO BEGIN
  436.                         OpenCPort(bitsPort); {always use a color port}
  437.                         portPixMap^^.pmTable := NIL; {in case we fail before we make a color table}
  438.                         
  439.                         IF requestedDepth > 0 THEN BEGIN {if there is a private device}
  440.                             {make a color table from the requested colors or a resource file or ROM}
  441.                             Out(MakeCleanColorTable(requestedColors, requestedDepth, colors));
  442.                             
  443.                             {now set up the pixmap of the port}
  444.                             InitGBufferPixmap(portPixMap, bounder, chunkyPix, requestedDepth, 1, requestedDepth,
  445.                                 colors, {returns} bitsSize);
  446.         
  447.                             {make a private device}
  448.                             bitsDevice := GDHandle(NewHandleClear(SIZEOF(GDevice)));
  449.                             Out(MemError); {must have failed because of heap paucity}
  450.         
  451.                             inverseTable := ITabHandle(NewHandle(0)); {create the inverse table}
  452.                             Out(MemError);
  453.                             preferredResolution := GetMainDevice^^.gdResPref; {preferred resolution ??? is this cool?}
  454.         
  455.                             WITH bitsDevice^^ DO BEGIN {set up the other fields of the device}
  456.                                 gdType := clutType;
  457.                                 gdITable := inverseTable;
  458.                                 gdResPref := preferredResolution;
  459.                                 gdFlags := 2 ** noDriver;
  460.                                 gdPMap := portPixMap;
  461.                             END; {WITH}
  462.                             InitGDevice(0, -1, bitsDevice); {sets gdRect from pixmap now; in future may do more}
  463.                             MakeITable(colors, inverseTable, preferredResolution);
  464.                             Out(QDError); {MakeITable failed}
  465.                         END ELSE BEGIN {when there is no private device}
  466.                                 {get the max. area device and set up the port’s pixmap accordingly}
  467.                                 IF NOT InitGBufferScreen(portPixMap, requestedBounds, bitsDevice, {returns} bitsSize) THEN
  468.                                     Out(noDeviceIntersectErr); {no devices intersect bounds}
  469.                                     
  470.                                 oldSeed := portPixMap^^.pmTable^^.ctSeed; {used to detect color updates}
  471.                         END;
  472.                         
  473.                     map := BitMapPtr(portPixMap^); {point to the map so we can set the portRect & visRgn}
  474.  
  475. {!!! ??? Do we need to call ForeColor and BackColor on the CGrafPort to set it up right?}
  476.  
  477.                     END {WITH}
  478.                 ELSE BEGIN {Classic QuickDraw}
  479.                     OpenPort(GrafPtr(bitsPort));
  480.                     WITH GrafPtr(bitsPort)^, portBits DO BEGIN
  481.                         bounds := bounder;
  482.                         {figure out how much space we need for our bits}
  483.                         WITH bounds DO BEGIN
  484.                             rowBytes := ((right + 15) DIV 16) * 2;
  485.                             {calculate an even # of words}
  486.                             bitsSize := bottom * LONGINT(rowBytes); {calculate the size}
  487.                         END; {WITH}
  488.                         map := @portBits; {point to the map so we can set it up}
  489.                     END; {WITH}
  490.                 END;
  491.                     
  492.                 WITH bitsPort^ DO BEGIN
  493.                     portRect := map^.bounds; {port's portRect is same as bounds}
  494.                     RectRgn(visRgn, portRect); {the visRgn must be changed}
  495.                     invalRegion := NewRgn;
  496.                     Out(MemError);
  497.                     CopyRgn(visRgn, invalRegion);
  498.                 END; {WITH}
  499.                 
  500.                 bits := Handle(NewImprovedGBuffer(bitsSize, needs32Bits, buffNotNeeded));
  501.                 notOurs := bits <> NIL;
  502.                 IF NOT notOurs THEN BEGIN
  503.                     IF requestedPolite THEN
  504.                         contiguousSpace := MaxBlock {make sure we have room without purging}
  505.                     ELSE
  506.                         PurgeSpace(totalSpace, contiguousSpace); {make sure we have room with purging}
  507.                     IF bitsSize + kOffscreenReserve > contiguousSpace THEN
  508.                         {if there is not enough room for the bits & reserve block, error out}
  509.                         Out(memFullErr);
  510.                     bits := NewHandleClear(bitsSize); {get space for the bits}
  511.                     Out(MemError);
  512.                     IF requestedPolite THEN
  513.                         HPurge(bits); {keep bits purgeable if we are polite}
  514.                 END;
  515.                 
  516.             END; {WITH}
  517.             HUnlock(offscreenHandle);
  518.  
  519.             SetPort(oldPort);
  520.         END; {IF GetMap(offscreenHandle) = NIL}
  521.  
  522.     AllocateBits := noErr;
  523. END; {AllocateBits}
  524.  
  525.  
  526.  
  527. PROCEDURE DisposeOffscreen(offscreenHandle: Handle);
  528. {Get rid of everything, including offscreenHandle itself.}
  529.  
  530. BEGIN {DisposeOffscreen}
  531.     IF offscreenHandle <> NIL THEN BEGIN
  532.         DeallocateBits(offscreenHandle);
  533.         DisposeHandle(Handle(PrivateHandle(offscreenHandle)^^.requestedColors));
  534.         DisposeHandle(offscreenHandle);
  535.     END; {IF offscreenHandle <> NIL}
  536. END; {DisposeOffscreen}
  537.  
  538.  
  539.  
  540. FUNCTION NewOffscreen(bounds: Rect; depth: INTEGER; colors: CTabHandle;
  541.                       memoryPolite: BOOLEAN; VAR buffNotNeeded: BOOLEAN; VAR offscreenHandle: Handle): OSErr;
  542. {Create an offscreenHandle. Return NIL if there is an error.}
  543.  
  544. VAR
  545.     aHandle    : Handle;
  546.  
  547.     PROCEDURE Out(error: OSErr);
  548.     BEGIN {Out}
  549.         IF error <> noErr THEN BEGIN
  550.             DisposeHandle(aHandle); {dispose handle if we made it}
  551.             NewOffscreen := error;
  552.             offscreenHandle := NIL;
  553.             EXIT(NewOffscreen);
  554.         END; {IF error <> noErr}
  555.     END; {Out}
  556.  
  557. BEGIN {NewOffscreen}
  558.     aHandle := NewHandleClear(SizeOf(PrivateRecord)); {make the private block}
  559.     Out(MemError);
  560.  
  561.     MoveHHi(aHandle); {fly this high so it doesn't fragment the heap}
  562.     HLock(aHandle);
  563.     WITH PrivateHandle(aHandle)^^ DO BEGIN
  564.         requestedBounds := bounds;
  565.         IF theMac.hasColorQD THEN
  566.             requestedDepth := depth
  567.         ELSE
  568.             requestedDepth := noDepth;
  569.         IF requestedDepth > 0 THEN BEGIN {if we need a private device}
  570.             Out(HandToHand(Handle(colors)));
  571.             requestedColors := colors;
  572.         END;
  573.         requestedPolite := memoryPolite;
  574.     END; {WITH}
  575.     HUnlock(aHandle);
  576.  
  577.     {now, we have filled in all of the fields of offscreenHandle}
  578.     NewOffscreen := AllocateBits(aHandle, buffNotNeeded);
  579.     offscreenHandle := aHandle;
  580. END; {NewOffscreen}
  581.  
  582.  
  583.  
  584. FUNCTION CheckOffscreen(offscreenHandle: Handle; VAR drawNeeded: BOOLEAN): OSErr;
  585.  
  586. VAR
  587.     error            : OSErr;
  588.     newSeed            : LONGINT;
  589.     buffNotNeeded    : BOOLEAN;
  590.  
  591. BEGIN {CheckOffscreen}
  592.     drawNeeded := TRUE; {default we must draw if offscreen invalid}
  593.     error := noErr; {no error either}
  594.     IF offscreenHandle <> NIL THEN
  595.         WITH PrivateHandle(offscreenHandle)^^ DO BEGIN
  596.             IF requestedDepth = kMaxDepth THEN {if it's not a private device}
  597.                 IF oldSeed <> bitsDevice^^.gdPMap^^.pmTable^^.ctSeed THEN {we have a color update, Houston}
  598.                     DeallocateBits(offscreenHandle);
  599.  
  600.             drawNeeded := GetMap(offscreenHandle) = NIL;
  601.             error := AllocateBits(offscreenHandle, buffNotNeeded); {allocate if deallocated}
  602.         END; {WITH}
  603.             
  604.     IF GetMap(offscreenHandle) <> NIL THEN
  605.         drawNeeded := drawNeeded | (NOT EmptyRgn(PrivateHandle(offscreenHandle)^^.invalRegion));
  606.         {can’t use the width for invalRegion since we may have done an AllocateBits}
  607.         
  608.     CheckOffscreen := error;
  609. END; {CheckOffscreen}
  610.  
  611.  
  612. PROCEDURE ValidRectOffscreen(offscreenHandle: Handle; window: WindowPtr; validRect: Rect);
  613.  
  614. VAR
  615.     validRgn    : RgnHandle;
  616.  
  617. BEGIN
  618.     validRgn := NewRgn;
  619.     RectRgn(validRgn, validRect);
  620.     ValidRgnOffscreen(offscreenHandle, window, validRgn);
  621.     DisposeRgn(validRgn);
  622. END; {ValidRectOffscreen}
  623.  
  624.  
  625. PROCEDURE ValidRgnOffscreen(offscreenHandle: Handle; window: WindowPtr; validRegion: RgnHandle);
  626.  
  627. VAR
  628.     savedPort    : GrafPtr;
  629.     
  630. BEGIN
  631.     IF window <> NIL THEN BEGIN
  632.         GetPort(savedPort);
  633.         SetPort(window);
  634.         ValidRgn(validRegion);
  635.         SetPort(savedPort);
  636.     END;
  637.     IF GetMap(offscreenHandle) <> NIL THEN
  638.         WITH PrivateHandle(offscreenHandle)^^ DO
  639.             DiffRgn(invalRegion, validRegion, invalRegion);
  640. END; {ValidRgnOffscreen}
  641.  
  642.  
  643. PROCEDURE InvalRectOffscreen(offscreenHandle: Handle; window: WindowPtr; invalidRect: Rect);
  644.  
  645. VAR
  646.     invalidRgn    : RgnHandle;
  647.  
  648. BEGIN
  649.     invalidRgn := NewRgn;
  650.     RectRgn(invalidRgn, invalidRect);
  651.     InvalRgnOffscreen(offscreenHandle, window, invalidRgn);
  652.     DisposeRgn(invalidRgn);
  653. END; {InvalRectOffscreen}
  654.  
  655.  
  656. PROCEDURE InvalRgnOffscreen(offscreenHandle: Handle; window: WindowPtr; invalidRgn: RgnHandle);
  657.  
  658. VAR
  659.     savedPort    : GrafPtr;
  660.     
  661. BEGIN
  662.     IF window <> NIL THEN BEGIN
  663.         GetPort(savedPort);
  664.         SetPort(window);
  665.         InvalRgn(invalidRgn);
  666.         SetPort(savedPort);
  667.     END;
  668.     IF GetMap(offscreenHandle) <> NIL THEN
  669.         WITH PrivateHandle(offscreenHandle)^^ DO
  670.             UnionRgn(invalRegion, invalidRgn, invalRegion);
  671. END; {ValidRgnOffscreen}
  672.  
  673.  
  674. FUNCTION CheckBoundsOffscreen(offscreenHandle: Handle; newBounds: Rect;
  675.                        VAR drawNeeded: BOOLEAN): OSErr;
  676.  
  677. VAR
  678.     somethingChanged    : BOOLEAN;
  679.     rightDevice            : GDHandle;
  680.  
  681. BEGIN {CheckBoundsOffscreen}
  682.     IF offscreenHandle <> NIL THEN
  683.         WITH PrivateHandle(offscreenHandle)^^ DO BEGIN
  684.             {check to see if the size of the port changed}
  685.             WITH newBounds, bitsPort^.portRect.botRight DO
  686.                 somethingChanged := (right - left <> h) | (bottom - top <> v);
  687.  
  688.             IF NOT somethingChanged THEN BEGIN
  689.                 {check to see if the depth of the screen changed}
  690.                 rightDevice := NIL;
  691.                 IF requestedDepth = kMaxDepth THEN
  692.                     rightDevice := GetMaxDevice(newBounds);
  693.                 somethingChanged := (rightDevice <> NIL) & (rightDevice <> bitsDevice);
  694.             END;
  695.  
  696.             IF somethingChanged THEN BEGIN {we have a color update, Houston}
  697.                 DeallocateBits(offscreenHandle);
  698.                 requestedBounds := newBounds;
  699.             END;
  700.         END; {WITH}
  701.     CheckBoundsOffscreen := CheckOffscreen(offscreenHandle, drawNeeded);
  702. END; {CheckBoundsOffscreen}
  703.  
  704.  
  705.  
  706. PROCEDURE BeginOffscreenDrawing(offscreenHandle: Handle; port: GrafPtr);
  707. BEGIN {BeginOffscreenDrawing}
  708.     IF GetMap(offscreenHandle) <> NIL THEN
  709.     {note side effect here of setting up map for drawing (stuffing baseAddr)}
  710.         WITH PrivateHandle(offscreenHandle)^^ DO BEGIN
  711.             IF NOT notOurs THEN {if we, not the system, allocated the bits}
  712.                 {lock the bits down}
  713.                 HLock(bits);
  714.  
  715.             {we need the pixmap locked while we are using it}
  716.             IF theMac.hasColorQD THEN
  717.                 HLock(Handle(bitsPort^.portPixMap));
  718.  
  719.             drawingPort := port;
  720.             GetPort(savedPort);
  721.             
  722.             {set up the port and the device}
  723.             
  724.             IF drawingPort = NIL THEN
  725.                 SetPort(GrafPtr(bitsPort))
  726.             ELSE BEGIN
  727.                 SetPort(drawingPort);
  728.                 {save the visRgn and replace it with our private one}
  729.                 savedVisRgn := drawingPort^.visRgn;
  730.                 drawingPort^.visRgn := bitsPort^.visRgn;
  731.                 
  732.                 savedMap := drawingPort^.portBits;
  733.                 
  734.                 IF theMac.hasColorQD THEN
  735.                     SetPortPix(bitsPort^.portPixMap)
  736.                 ELSE
  737.                     SetPortBits(GrafPtr(bitsPort)^.portBits);
  738.             END; {for caller-supplied port case}
  739.             
  740.             IF theMac.hasColorQD THEN BEGIN
  741.                 savedDevice := GetGDevice;
  742.                 SetGDevice(bitsDevice);
  743.             END; {IF theMac.hasColorQD}
  744.             
  745.             WITH qd.thePort^ DO
  746.                 {intersect the invalidated offscreen region with private visRgn}
  747.                 SectRgn(invalRegion, visRgn, visRgn);
  748.         END; {WITH}
  749. END; {BeginOffscreenDrawing}
  750.  
  751.  
  752.  
  753. PROCEDURE EndOffscreenDrawing(offscreenHandle: Handle);
  754.  
  755. TYPE
  756.     PMapHandlePtr    = ^PixMapHandle;
  757.     
  758. BEGIN {EndOffscreenDrawing}
  759.     IF GetMap(offscreenHandle) <> NIL THEN
  760.         WITH PrivateHandle(offscreenHandle)^^ DO BEGIN
  761.             {restore the map, device, port}
  762.             
  763.             IF drawingPort <> NIL THEN BEGIN
  764.                 SetPort(drawingPort);
  765.                 drawingPort^.visRgn := savedVisRgn; {restore original visRgn}
  766.                 
  767.                 IF theMac.hasColorQD THEN
  768.                     SetPortPix(PMapHandlePtr(@savedMap)^)
  769.                 ELSE
  770.                     SetPortBits(savedMap);
  771.             END; {for caller-supplied port case}                
  772.                 
  773.             IF theMac.hasColorQD THEN
  774.                 SetGDevice(savedDevice);
  775.                 
  776.             SetPort(savedPort);
  777.  
  778.             {we only need the pixmap locked while we are using it}
  779.             IF theMac.hasColorQD THEN
  780.                 HUnlock(Handle(bitsPort^.portPixMap));
  781.  
  782.             IF NOT notOurs THEN
  783.                 {unlock the bits so they can float}
  784.                 HUnlock(bits);
  785.             
  786.             WITH bitsPort^ DO
  787.                 RectRgn(visRgn, portRect); {set private visRgn back to full size}
  788.         END; {WITH}
  789. END; {EndOffscreenDrawing}
  790.  
  791.  
  792. PROCEDURE BeginUpdateOffscreen(offscreenHandle: Handle; window: WindowPtr);
  793. BEGIN
  794.     BeginUpdate(window);
  795.     IF GetMap(offscreenHandle) <> NIL THEN BEGIN
  796.         BeginOffscreenDrawing(offscreenHandle, window); {’sects invalRegion with visRgn}
  797.         {validate the entire contents of the offscreen buffer}
  798.         ValidRectOffscreen(offscreenHandle, NIL, PrivateHandle(offscreenHandle)^^.bitsPort^.portRect);
  799.     END;
  800. END; {BeginUpdateOffscreen}
  801.  
  802.  
  803. PROCEDURE EndUpdateOffscreen(offscreenHandle: Handle; window: WindowPtr);
  804. BEGIN
  805.     IF GetMap(offscreenHandle) <> NIL THEN BEGIN
  806.         EndOffscreenDrawing(offscreenHandle);
  807.         
  808.         SetPort(window);
  809.         ForeColor(blackColor);
  810.         BackColor(whiteColor);
  811.         {now copy from the offscreen buffer to the window (clipped to window’s visRgn)}
  812.         WITH PrivateHandle(offscreenHandle)^^ DO
  813.             CopyBits(GrafPtr(bitsPort)^.portBits, window^.portBits, bitsPort^.portRect,
  814.                 window^.portRect, srcCopy, NIL);
  815.     END;
  816.     EndUpdate(window);
  817. END; {EndUpdateOffscreen}
  818.  
  819.  
  820. {Create an off-screen pixel map for a port.
  821.  It will always use kMaxDepth and always be memoryPolite.}
  822.  
  823. FUNCTION NewOffscreenForWindow(window: WindowPtr; VAR buffNotNeeded: BOOLEAN;
  824.                                VAR offscreenHandle: Handle): OSErr;
  825.  
  826. VAR
  827.     savedPort    : GrafPtr;
  828.     globalRect    : Rect;
  829.  
  830. BEGIN {NewOffscreenForWindow}
  831.     SetPort(window);
  832.  
  833.     globalRect := window^.portRect; {calculate a global rect for this window}
  834.     WITH globalRect DO BEGIN
  835.         LocalToGlobal(topLeft);
  836.         LocalToGlobal(botRight);
  837.     END;
  838.  
  839.     NewOffscreenForWindow := NewOffscreen(globalRect, kMaxDepth, NIL, TRUE,
  840.         buffNotNeeded, offscreenHandle);
  841. END; {NewOffscreenForWindow}
  842.  
  843.  
  844.  
  845. (* ??? looks like these aren’t needed…
  846. {Begin an update for the window specified, using the specified off-screen
  847.  handle for bits. This sets the port to the window! …and temporarily nukes
  848.  the visRgn! ??? @@@ !!!}
  849.  
  850. PROCEDURE BeginOffscreenUpdate(offscreenHandle: Handle; window: WindowPtr);
  851. BEGIN {BeginOffscreenUpdate}
  852.     BeginOffscreenDrawing(offscreenHandle); {???!!! 
  853.     SetPort(window); {override the offscreen port which was set by BOU}
  854.     BeginUpdate(window);
  855.     
  856. END; {BeginOffscreenUpdate}
  857.  
  858.  
  859.  
  860. {Finish drawing to an off-screen port. Also copy the bits from the
  861.  port to the screen.}
  862.  
  863. PROCEDURE EndOffscreenUpdate(offscreenHandle: Handle; window: WindowPtr);
  864.  
  865.  
  866.  
  867. {Call this when the rectangle of the off-screen map needs to change, perhaps
  868.  due to a window moving on the main screen. This will also check for any change
  869.  in depth.}
  870.  
  871. FUNCTION UpdateOffscreenForWindow(offscreenHandle: Handle; window: WindowPtr;
  872.                                   VAR drawNeeded: BOOLEAN): OSErr;
  873. *)
  874.  
  875.  
  876. END. {Offscreen}
  877.